package 零钱兑换;

import java.util.Arrays;

/**
 * createTime: 2025/5/11 10:58
 * description:
 * 给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额，返回 -1。
 * 你可以认为每种硬币的数量是无限的。
 * 示例 1：
 * 输入：coins = [1, 2, 5], amount = 11
 * 输出：3
 * 解释：11 = 5 + 5 + 1
 *
 * @author AirMan
 */
public class Solution {
    public int coinChange(int[] coins, int amount) {
        // 完全背包
        // ① dp数组及其含义：dp[j]表示凑足总额为 j 所需钱币的最少个数为 dp[j]
        // ② 动态转移方程：dp[j] = Math.min(dp[j], dp[j-coins[i]] + 1);
        // ③ dp数组的初始化：dp[0] = 0，凑足金额为0，需要0个硬币
        //      考虑到递推公式的特性，dp[j]必须初始化为一个最大的数，否则就会在min(dp[j - coins[i]] + 1,
        //      dp[j])比较的过程中被初始值覆盖。所以下标非0的元素都是应该是最大值。
        // ④ 遍历顺序：先遍历coins，再遍历amount。或者先遍历amount，再遍历coins都是可以的
        // ⑤ 举例推导dp数组：coins = [1, 2, 5], amount = 5
        //      index:      0   1   2   3   4   5
        //                  0   1   1   2   3   4
        //                  0   1   1   2   2   3
        //                  0   1   1   2   2   1

        int[] dp = new int[amount + 1];
        // init dp
        Arrays.fill(dp, Integer.MAX_VALUE);
        dp[0] = 0;

        // Traversal coins
        for (int i = 0; i < coins.length; i++) {
            // Traversal amount
            for (int j = coins[i]; j <= amount; j++) {
                if (dp[j - coins[i]] != Integer.MAX_VALUE) {
                    dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1);
                }
            }
            System.out.println(Arrays.toString(dp));
        }

        return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount];
    }

}
